// GeoCash.cpp : Defines the entry point for the application.
//

#include "windows.h"
#include "aygshell.h"
#include "winnls.h"
#include <htmlctrl.h>

#include "GeoCash.h"
#include "GPS.h"

#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))

///////////////////////////////////////////////////////////////////////////////
// Masked Edit Control (only allow currency related characters)

static LRESULT CALLBACK MaskedEditProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static TCHAR szAllowableChars[4096] = {0};

	// Fetch the old wnd proc which we stored away in the GWL_USERDATA field
	WNDPROC pfnOldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_USERDATA);

	// Process messages of interest
	if (uMsg == WM_CHAR)
	{
		// Is this the first key press in the lifetime of this app?
		if (szAllowableChars[0] == '\0')
		{
			// If so generate a list of special characters which
			// may be used in a currency or decimal value. We
			// cache this string to avoid performing these queries
			// for each and every key press.
#define APPEND_LOCALE_INFO(buffer, lctype) (GetLocaleInfo(lcid, lctype, &buffer[_tcslen(buffer)], ARRAY_SIZE(buffer) - _tcslen(buffer)))

			LCID lcid = GetUserDefaultLCID();
			APPEND_LOCALE_INFO(szAllowableChars, LOCALE_SCURRENCY);
			APPEND_LOCALE_INFO(szAllowableChars, LOCALE_SMONDECIMALSEP);
			APPEND_LOCALE_INFO(szAllowableChars, LOCALE_SMONTHOUSANDSEP);
			APPEND_LOCALE_INFO(szAllowableChars, LOCALE_SDECIMAL);
			APPEND_LOCALE_INFO(szAllowableChars, LOCALE_STHOUSAND);
			APPEND_LOCALE_INFO(szAllowableChars, LOCALE_SPOSITIVESIGN);
			APPEND_LOCALE_INFO(szAllowableChars, LOCALE_SNEGATIVESIGN);
		}

		// Is this an acceptable character?
		if (!iswdigit(wParam)  // digits
			&& !wcschr(szAllowableChars, wParam) // special symbols
			&& wParam != '\b') // backspace
		{
			// No - then stop it from being processed
			// by the original window procedure.
			return 0;
		}
	}

	// Allow the original window procedure to process
	// the message
	return CallWindowProc(pfnOldWndProc, hWnd, uMsg, wParam, lParam);
}

// Configure an edit control so that we can filter
// the allowable set of characters the user can
// type into the field.
static void SetupEditControl(HWND hDlg, UINT id)
{
	// Find the window handle for the edit control
	HWND hWndCtrl = GetDlgItem(hDlg, id);

	// Store the current window procedure in the
	// GWL_USERDATA field
	WNDPROC pfnOldWndProc = (WNDPROC)GetWindowLong(hWndCtrl, GWL_WNDPROC);
	SetWindowLong(hWndCtrl, GWL_USERDATA, (LONG)pfnOldWndProc);

	// Replace the window procedure with our one which
	// filters the allowable key presses
	SetWindowLong(hWndCtrl, GWL_WNDPROC, (LONG)MaskedEditProc);
}

///////////////////////////////////////////////////////////////////////////////

INT_PTR CALLBACK GeoCashDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	// A buffer to append each log entry onto the end of
	static UINT iMaxLogSize = 4096;
	static TCHAR * pszLog = (TCHAR*)malloc(iMaxLogSize * sizeof(TCHAR));

	static SHACTIVATEINFO sai = { sizeof(SHACTIVATEINFO) };

	SHInputDialog(hDlg, message, wParam);

    switch (message)
    {
        case WM_INITDIALOG:
            {
				_tcscpy(pszLog, _T(""));

                // Create a Done button and size it.  
                SHINITDLGINFO shidi;
				SHMENUBARINFO mbi;

                shidi.dwMask = SHIDIM_FLAGS;
                shidi.dwFlags = SHIDIF_DONEBUTTON | SHIDIF_SIZEDLGFULLSCREEN;
                shidi.hDlg = hDlg;
                SHInitDialog(&shidi);

				// Create a menu bar
                memset(&mbi, 0, sizeof(SHMENUBARINFO));
				mbi.cbSize     = sizeof(SHMENUBARINFO);
				mbi.hwndParent = hDlg;
				mbi.nToolBarId = IDR_MENU;
				mbi.hInstRes   = GetModuleHandle(NULL);
				mbi.dwFlags = SHCMBF_HMENU;
	            SHCreateMenuBar(&mbi);

				// Subclass the amount edit control so we only
				// allowable characters acceptable in a decimal
				// or currency value.
				SetupEditControl(hDlg, IDC_EDIT_AMOUNT);

				// Pretend the user selected the Start GPS menu item
				// so that the application starts off with the GPS
				// enabled
				PostMessage(hDlg, WM_COMMAND, IDM_GPS_START, 0);
			}
            return (INT_PTR)TRUE;

		case WM_ACTIVATE:
			SHHandleWMActivate(hDlg, wParam, lParam, &sai, SHA_INPUTDIALOG);
			break;

		case WM_SETTINGCHANGE:
			SHHandleWMSettingChange(hDlg, wParam, lParam, &sai);
			break;

		case WM_SIZE:
			{
				// Resize the HTML control so that it doesn't
				// run off the end of dialog (when it is resized
				// due to the SIP being displayed).
				HWND hWndHTML = GetDlgItem(hDlg, IDC_HTMLVIEW);
				RECT rcBounds;

				GetWindowRect(hWndHTML, &rcBounds);
				MapWindowPoints(NULL, hDlg, (LPPOINT)&rcBounds, 2);
				
				MoveWindow(hWndHTML, 0, rcBounds.top, LOWORD(lParam), HIWORD(lParam) - rcBounds.top, TRUE);
			}
			break;

		case WM_INITMENUPOPUP:
			// The menu is about to popup so enable/disable
			// the various menu items depending upon the state
			// of the application.
			if (GPS_IsOpened())
			{
				EnableMenuItem((HMENU)wParam, IDM_GPS_START, MF_BYCOMMAND | MF_GRAYED);
				EnableMenuItem((HMENU)wParam, IDM_GPS_STOP,  MF_BYCOMMAND | MF_ENABLED);
			}
			else
			{
				EnableMenuItem((HMENU)wParam, IDM_GPS_START, MF_BYCOMMAND | MF_ENABLED);
				EnableMenuItem((HMENU)wParam, IDM_GPS_STOP,  MF_BYCOMMAND | MF_GRAYED);
			}
			break;

        case WM_COMMAND:
            switch (LOWORD(wParam))
			{
				case IDOK:
		            EndDialog(hDlg, LOWORD(wParam));
					return (INT_PTR)TRUE;

				case IDM_GPS_START:
					if (!GPS_IsOpened())
					{
						GPS_Open();
					}
					break;
           
				case IDM_GPS_STOP:
					if (GPS_IsOpened())
					{
						GPS_Close();
					}
					break;

				case IDM_CLEAR:
					// Clear the log
					_tcscpy(pszLog, _T(""));
					SendDlgItemMessage(hDlg, IDC_HTMLVIEW, DTM_ADDTEXTW, 0, (LPARAM)pszLog);
					SendDlgItemMessage(hDlg, IDC_HTMLVIEW, DTM_ENDOFSOURCE, 0, 0);
					break;

				case IDM_LOG:
					{
						static TCHAR szNewLogEntry[16 * 1024];
						static TCHAR szAmount[64];
						static TCHAR szNote[2048];
						static TCHAR szDate[64];
						static TCHAR szTime[64];
						static TCHAR szLatitude[256];
						static TCHAR szLongitude[256];
						SYSTEMTIME st;
						GPS_POSITION gpsPosition;

						// Get the current amount and note from the edit controls
						GetDlgItemText(hDlg, IDC_EDIT_AMOUNT, szAmount, ARRAY_SIZE(szAmount));						
						GetDlgItemText(hDlg, IDC_EDIT_NOTE, szNote, ARRAY_SIZE(szNote)); 

						// Convert the current date/time into text
						GetLocalTime(&st);
						GetDateFormat(GetUserDefaultLCID(), DATE_SHORTDATE, &st, NULL, szDate, ARRAY_SIZE(szDate));
						GetTimeFormat(GetUserDefaultLCID(), TIME_NOSECONDS, &st, NULL, szTime, ARRAY_SIZE(szTime));

						// Obtain the current GPS position information
						GPS_GetData(&gpsPosition, NULL);

						if ((gpsPosition.dwValidFields & GPS_VALID_LATITUDE)
							&& (gpsPosition.dwValidFields & GPS_VALID_LONGITUDE))
						{
							GPS_FormatDegreesMinutesSeconds(szLatitude, gpsPosition.dblLatitude);
							GPS_FormatDegreesMinutesSeconds(szLongitude, gpsPosition.dblLongitude);
						}
						else
						{
							_tcscpy(szLatitude, _T("Unknown"));
							_tcscpy(szLongitude, _T("Unknown"));
						}

						// Format into an HTML snippet and append
						// to the end of the log
						_stprintf(szNewLogEntry, _T("<b>Date:</b> %s %s<br/>") \
							_T("<b>Amount:</b> <span style=\"color:red\">%s</span><br/>") \
							_T("<b>Note:</b> %s<br/>") \
							_T("<b>Latitude:</b> %s<br/>") \
							_T("<b>Longitude:</b> %s<br/><br/>"),
							szDate, szTime,
							szAmount, szNote,
							szLatitude, szLongitude);

						if (_tcslen(pszLog) + _tcslen(szNewLogEntry) + 1 > iMaxLogSize)
						{
							// Adding this entry would exceed our current buffer
							// so increase the size by an additional 4096 characters
							iMaxLogSize += 4096;
							pszLog = (TCHAR*)realloc(pszLog, iMaxLogSize * sizeof(TCHAR));
						}
						_tcscat(pszLog, szNewLogEntry);

						SendDlgItemMessage(hDlg, IDC_HTMLVIEW, DTM_ADDTEXTW, 0, (LPARAM)pszLog);
						SendDlgItemMessage(hDlg, IDC_HTMLVIEW, DTM_ENDOFSOURCE, 0, 0);
					}
					break;
			}
            break;
		
        case WM_CLOSE:
			if (GPS_IsOpened())
			{
				GPS_Close();
			}

            return (INT_PTR)TRUE;
    }
    return (INT_PTR)FALSE;
}

int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPTSTR    lpCmdLine,
                   int       nCmdShow)
{
	InitHTMLControl(hInstance);

	return DialogBox(hInstance, (LPCTSTR)IDD_GEOCASH, NULL, GeoCashDialog);
}